
//
// This is the Reader class it will read a given file
// and parse it to its internal structures
//

package br.unirio.bsi.dsw.AuctionLoader;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.lang.reflect.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import br.unirio.bsi.dsw.AuctionSite.model.fetch_parameter;
        
/**
 *
 * @author Henrique
 */
public class Reader<T>
{
  // to store type ref
  Class<T> _clazz;
  
  // to store list of structures
  private ArrayList<T> _gen_list = new ArrayList<T>();
    
  /**
   * Gets the list of generics entities found.
   * @return list of files found
   */
  public T[] getGenericsList()
  {
    T[] a = (T[]) Array.newInstance( _clazz, _gen_list.size() );
    for( int i = 0; i < a.length; i++ )
      a[i] = _gen_list.get(i);
    return a;
  }

  /**
   * This method parses a list of T generics in a txt.
   * And creates the structures it represents
   * @param path path to the file
   * @param separator the character used to separate fields inside each file
   * @param klazz cuz java generics are not reified 
   * @throws java.lang.IllegalArgumentException
   * @throws java.io.FileNotFoundException
   * @throws java.io.IOException
   * @throws java.text.ParseException
   */
  public Reader( String path, Character separator, Class<T> klazz ) throws
                                                    IllegalArgumentException,
                                                    FileNotFoundException,
                                                    IOException,
                                                    InstantiationException, 
                                                    IllegalAccessException, 
                                                    InvocationTargetException, 
                                                    ClassNotFoundException, 
                                                    NoSuchMethodException
  {
    // checks for invalid parameter
    if( path == null || path.isEmpty() || separator == null )
      throw new IllegalArgumentException("invalid parameters specified");
       
    // save type ref 
    _clazz = klazz;
            
    // create regex format for class
    Pattern T_regex_fmt = Pattern.compile( getClassRegexFormat( klazz, separator ) );
    
    // dafault file naming conventions
    String filename = path + klazz.getSuperclass().getSimpleName() + "s.txt";

    // opens input stream
    BufferedReader input = new BufferedReader( new FileReader( filename ) );
            
    // read file line by line
    String line = null;
    while( (line = readLine( input )) != null )
    {
      Matcher m = T_regex_fmt.matcher( line );
      if( m.matches() )
      {        
        // alloc generics
        T t = klazz.newInstance();

        // get model class fields
        Field fields[] = t.getClass().getSuperclass().getDeclaredFields();               
                
        // call setter methods 
        for( Field f : fields )
        {
          fetch_parameter rp = f.getAnnotation( fetch_parameter.class );
          if( rp != null && rp.txt_order() != -1 )
          {       
            Class param[] = new Class[]{f.getType()};
            Method met = t.getClass().getSuperclass().getDeclaredMethod( "set"+f.getName(), param );
            
            String value = m.group( rp.txt_order() + 1 );
          
            if( param[0] == Integer.class )                    
              met.invoke( t, Integer.parseInt( value ) );
            else
            if( param[0] == String.class )
              met.invoke( t, value ); 
            else
            if( param[0] == ArrayList.class )
            {
              int c_order = rp.txt_order();
              String gs[] = m.group( c_order ).split("\\x" + Integer.toHexString( separator ));
              
              // find the last assigned method
              for( Field mt : fields ) {                
                rp = mt.getAnnotation( fetch_parameter.class );
                if( rp != null && rp.txt_order() == c_order - 1 && gs.length > 1 ) {
                  t.getClass().getSuperclass().getDeclaredMethod( "set"+mt.getName(), new Class[]{mt.getType()} ).invoke( t, gs[0] );
                  break;
                }       
              }

              // save the rest
              ArrayList<String> a = new ArrayList<String>();                            
              for( int i = 1; i < gs.length; i++ )
                a.add(gs[i]);                            
              a.add( m.group( c_order + 1 ) );
                            
              // sets the list
              met.invoke( t, a );              
            }
          }   
        }
        
        // add to the list
        _gen_list.add(t);
      }
      else
        System.out.println("could not match line:"+line);
     }
     // Close the input stream
    input.close();
  }

  /**
   * helper
   * Reads a line from the input stream.
   * Ignores empty lines
   * @param input input stream to read
   * @return line of text from input
   * @throws java.io.IOException
   */
  private String readLine( BufferedReader input ) throws IOException
  {
    String line = input.readLine();
    if( line != null )
    {
      line = line.trim();
      if( line.isEmpty() )
        line = readLine( input );
    }
    return line;
  }
    /**
   * helper
   * Returns the regular expression format for a class
   * @param  clazz  class to generate
   * @param separator the character used to separate fields inside each file
   * @return s      regex for the class
   */
  private String getClassRegexFormat( Class<T> klazz, Character separator )
  {
    if( klazz == null  )
      return null;
    
    Field fields[] = klazz.getSuperclass().getDeclaredFields();
    Field fannot[] = new Field[ fields.length ];
        
    int c = 0;
    for( Field f : fields )
    {      
      fetch_parameter rp = f.getAnnotation( fetch_parameter.class );
      if( rp != null && rp.txt_order() != -1 ) {
        c++;
        fannot[ rp.txt_order() ] = f;
      }
    }
       
    String dyn_regex = "^";

    for( int i = 0; i < c; i++ )
    {
      Field f = fannot[i];
    
      if( f.getType() == Integer.class )
        dyn_regex += "(\\d+)";
      else
      if( f.getType() == String.class || f.getType() == ArrayList.class )
        dyn_regex += "(.+)";
      
      if( i < c - 1 )
        dyn_regex += "\\x" + Integer.toHexString( separator );      
    }
    
    dyn_regex += "$";

    return dyn_regex;
  }  
}
